package com.tsj.spzx.order.service.Impl;

import cn.hutool.core.collection.CollectionUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tsj.spzx.client.feign.ProductFeignClient;
import com.tsj.spzx.client.feign.UserAddressFeignClient;
import com.tsj.spzx.exception.TsjException;
import com.tsj.spzx.fegin.cart.CartFeignClient;
import com.tsj.spzx.model.dto.h5.OrderInfoDto;
import com.tsj.spzx.model.entity.h5.CartInfo;
import com.tsj.spzx.model.entity.order.OrderInfo;
import com.tsj.spzx.model.entity.order.OrderItem;
import com.tsj.spzx.model.entity.order.OrderLog;
import com.tsj.spzx.model.entity.product.ProductSku;
import com.tsj.spzx.model.entity.user.UserAddress;
import com.tsj.spzx.model.entity.user.UserInfo;
import com.tsj.spzx.model.vo.common.ResultCodeEnum;
import com.tsj.spzx.model.vo.h5.TradeVo;
import com.tsj.spzx.order.mapper.OrderItemMapper;
import com.tsj.spzx.order.mapper.OrderLogMapper;
import com.tsj.spzx.order.mapper.OrderMapper;
import com.tsj.spzx.order.service.OrderService;
import com.tsj.spzx.utils.AuthContextThreadLocalUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private OrderLogMapper orderLogMapper;
    @Autowired
    private CartFeignClient cartFeignClient;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private UserAddressFeignClient userAddressFeignClient;

    /**
     * 确认下单
     * @return TradeVo 对象，封装的是订单总金额 和 订单项（点击结算时，购物车中选择的商品）信息
     */
    @Override
    public TradeVo trade() {
        // todo：远程调用，获取当前用户购物车选中的商品信息
        List<CartInfo> cartInfoList = cartFeignClient.getAllCkecked();
        // 将商品信息封装到TradeVo中
        ArrayList<OrderItem> orderItemList = new ArrayList<>();
        BigDecimal totalAmount = new BigDecimal(0);  // 计算订单总金额
        for (CartInfo cartInfo : cartInfoList) {
            OrderItem orderItem = new OrderItem();
            orderItem.setSkuId(cartInfo.getSkuId());
            orderItem.setSkuName(cartInfo.getSkuName());
            orderItem.setSkuPrice(cartInfo.getCartPrice());
            orderItem.setSkuNum(cartInfo.getSkuNum());
            orderItem.setThumbImg(cartInfo.getImgUrl());
            orderItemList.add(orderItem);

            totalAmount = totalAmount.add(cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum())));
        }

        TradeVo tradeVo = new TradeVo();
        tradeVo.setTotalAmount(totalAmount);
        tradeVo.setOrderItemList(orderItemList);

        return tradeVo;
    }

    /**
     * 提交订单
     * @param orderInfoDto
     * @return
     */
    @Transactional
    @Override
    public Long submitOrder(OrderInfoDto orderInfoDto) {
        // 1、获取orderInfoDto中的订单项
        List<OrderItem> orderItemList = orderInfoDto.getOrderItemList();
        if (CollectionUtil.isEmpty(orderItemList)){
            // 如果订单项为空，则抛出异常
            throw new TsjException(ResultCodeEnum.DATA_ERROR);
        }
        // 如果不为空
        // 获取订单项中每个商品 skuId，远程调用，判断商品库存是否充足
        // 顺便获取商品总金额
        BigDecimal totalAmount = new BigDecimal(0);
        ArrayList<Long> skuIdList = new ArrayList<>();
        for (OrderItem orderItem : orderItemList) {
            skuIdList.add(orderItem.getSkuId());
            totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
        }
        // 获取到skuId集合
        // todo 远程调用：根据skuId集合获取productSku集合
        List<ProductSku> productSkuList = productFeignClient.findProductBySkuIdList(skuIdList);
        // 我们的目的是让productSkuList中的productSku顺序和skuIdList中的skuId保持一致(最终目的就是只查询一次数据库)
        // 再准备一个新集合存放对应顺序的productSku
        List<ProductSku> orderedProductSkuList = new ArrayList<>();
        Map<Long, ProductSku> skuMap = new HashMap<>();
        for (ProductSku productSku : productSkuList) {
            skuMap.put(productSku.getId(), productSku);
        }
        for (Long skuId : skuIdList) {
            // 按照skuId顺序加入到新集合，这时是productSkuList中的productSku顺序和skuIdList中的skuId保持一致
            orderedProductSkuList.add(skuMap.get(skuId));
        }
        // 这时，就可以判断商品库存是否充足 我们已经得到了skuId 和 productSku 一一对应的关系了，实际就是productSku和orderItem一一对应了
        for (int i = 0; i < orderItemList.size(); i++) {
            OrderItem orderItem = orderItemList.get(i);
            ProductSku productSku = orderedProductSkuList.get(i);
            // 如果为null，则抛出异常
            if(orderItem == null){
                throw new TsjException(ResultCodeEnum.DATA_ERROR);
            }

            // 如果大于库存数量，则抛出异常
            if (orderItem .getSkuNum() > productSku.getStockNum()){
                throw new TsjException(ResultCodeEnum.STOCK_LESS);
            }
        }

        // todo 2、远程调用,根据AddressId获取地址信息
        Long userAddressId = orderInfoDto.getUserAddressId();
        UserAddress userAddress = userAddressFeignClient.getAddressById(userAddressId);

        // 3、开始封装数据 向orderInfoDto中添加数据
        OrderInfo orderInfo = new OrderInfo();
        UserInfo userInfo = AuthContextThreadLocalUtil.getUserInfo();
        orderInfo.setUserId(userInfo.getId());
        orderInfo.setNickName(userInfo.getNickName());
        orderInfo.setOrderNo(String.valueOf(System.currentTimeMillis()));
        // 用户地址
        orderInfo.setReceiverName(userAddress.getName());
        orderInfo.setReceiverPhone(userAddress.getPhone());
        orderInfo.setReceiverTagName(userAddress.getTagName());
        orderInfo.setReceiverProvince(userAddress.getProvinceCode());
        orderInfo.setReceiverCity(userAddress.getCityCode());
        orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
        orderInfo.setReceiverAddress(userAddress.getFullAddress());
        // 订单金额
        orderInfo.setTotalAmount(totalAmount);
        orderInfo.setCouponAmount(new BigDecimal(0));    // 优惠卷
        orderInfo.setOriginalTotalAmount(totalAmount);       // 原价金额
        orderInfo.setFeightFee(orderInfoDto.getFeightFee()); // 运费
        orderInfo.setPayType(2);                             // 支付方式
        orderInfo.setOrderStatus(0);                        // 订单状态
        // 添加数据到order_info表中
        orderMapper.save(orderInfo);

        // 4、保存订单项数据 到 order_item表
        // 为每个订单项设置订单id
        for (OrderItem orderItem : orderItemList) {
            orderItem.setOrderId(orderInfo.getId());
        }
        orderItemMapper.save(orderItemList);

        // 5、保存订单日志数据到 order_log表中
        OrderLog orderLog = new OrderLog();
        orderLog.setOrderId(orderInfo.getId());
        orderLog.setProcessStatus(0);
        orderLog.setNote("提交订单");
        orderLogMapper.save(orderLog);

        // 6、删除购物车中所有被勾选的商品
        cartFeignClient.deleteCartChecked();

        // 7、返回订单id
        return orderInfo.getId();
    }

    /**
     * 根据订单id获取订单信息
     * @param orderId
     */
    @Override
    public OrderInfo getOrderInfoById(Long orderId) {
        OrderInfo orderInfo = orderMapper.getOrderInfoById(orderId);
        return orderInfo;
    }

    /**
     * 立即购买, 返回交易信息
     * @param skuId 商品skuId
     * @return
     */
    @Override
    public TradeVo buy(Long skuId) {
       // todo 远程调用，根据skuId获取商品信息
        ProductSku productSku = productFeignClient.getBySkuId(skuId).getData();
        // 封装数据
        OrderItem orderItem = new OrderItem();
        orderItem.setSkuId(skuId);
        orderItem.setSkuName(productSku.getSkuName());
        orderItem.setSkuNum(1);
        orderItem.setSkuPrice(productSku.getSalePrice());
        orderItem.setThumbImg(productSku.getThumbImg());

        List<OrderItem> orderItemList = new ArrayList<>();
        orderItemList.add(orderItem);

        TradeVo tradeVo = new TradeVo();
        BigDecimal totalAmount = orderItem.getSkuPrice();
        tradeVo.setTotalAmount(totalAmount);
        tradeVo.setOrderItemList(orderItemList);
        return tradeVo;
    }

    /**
     * 根据订单状态获取用户订单
     * @param page
     * @param limit
     * @param orderStatus
     * @return
     */
    /**
     * 那也就是说，我第一次查询时，pageHelper先查询总数据数为5，但我要返回10条，他最终就返回了5条给我，
     * 第二次查寻第2页时，他又去查询了总数，发现是5条，那也就表示第二页没有数据，SQL明确要求跳过前10条（OFFSET 10），而总数据只有5条，所以直接返回空列表
     * 然后for循环发现是空列表，也不执行循环语句，也就导致没有报错，最终返回给前端一个空列表，前端根据空列表判断，显示 没有更多数据了
     */
    @Override
    public PageInfo<OrderInfo> getOrderPageList(Integer page, Integer limit, Integer orderStatus) {
        PageHelper.startPage(page, limit);
        // 根据用户id和订单状态查询用户所有订单
        Long userId = AuthContextThreadLocalUtil.getUserInfo().getId();
        List<OrderInfo> orderInfoList = orderMapper.getOrderList(userId, orderStatus);

        // 一个订单可能有多个订单项
        // 根据订单获取该订单的所有订单项    在订单展示页面，展示的既有订单信息，也有订单项信息
        for (OrderInfo orderInfo : orderInfoList) {
            List<OrderItem> orderItemList = orderItemMapper.getOrderItemList(orderInfo.getId());
            orderInfo.setOrderItemList(orderItemList);
        }

        return new PageInfo<>(orderInfoList);
    }

    /**
     * 根据订单编号查询订单信息
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        OrderInfo orderInfo = orderMapper.getOrderInfoByOrderNo(orderNo);
        // 根据订单id查询 订单的订单项
        List<OrderItem> orderItemList = orderItemMapper.getOrderItemList(orderInfo.getId());
        orderInfo.setOrderItemList(orderItemList);
        return orderInfo;
    }

    /**
     * 支付成功后，修改订单状态
     * @param orderNo
     */
    @Override
    public void updateOrderInfoStatusPayed(String orderNo) {
        // 先根据订单编号查询订单信息
        OrderInfo orderInfo = orderMapper.getOrderInfoByOrderNo(orderNo);
        orderInfo.setOrderStatus(1);
        orderInfo.setPayType(2);                 // 支付方式
        orderInfo.setPaymentTime(new Date());   // 支付时间
        orderMapper.updateOrderInfo(orderInfo);

        // 记录日志
        OrderLog orderLog = new OrderLog();
        orderLog.setOrderId(orderInfo.getId());
        orderLog.setProcessStatus(1);       // 订单状态
        orderLog.setNote("支付成功");
        orderLogMapper.save(orderLog);
    }
}
